<!DOCTYPE html>
<html lang=zh>
<head>
    <meta charset="utf-8">
    
    <title>第三十九章：基于SpringBoot &amp; Quartz完成定时任务分布式单节点持久化 | 恒宇少年De成长之路</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <meta name="description" content="定时任务在企业项目比较常用到，几乎所有的项目都会牵扯该功能模块，定时任务一般会处理指定时间点执行某一些业务逻辑、间隔时间执行某一些业务逻辑等。我们在之前有讲过SpringBoot是已经集成了定时任务的，详见：第二十六章：SpringBoot使用@Scheduled创建定时任务，那么我们本章将会采用外置的quartz定时任务框架来完成定时任务的分布式单节点持久化，我们为什么要持久化定时任务呢？">
<meta name="keywords" content="SpringBoot">
<meta property="og:type" content="article">
<meta property="og:title" content="第三十九章：基于SpringBoot &amp; Quartz完成定时任务分布式单节点持久化">
<meta property="og:url" content="http://blog.yuqiyu.com/spring-boot-chapter-39/index.html">
<meta property="og:site_name" content="恒宇少年De成长之路">
<meta property="og:description" content="定时任务在企业项目比较常用到，几乎所有的项目都会牵扯该功能模块，定时任务一般会处理指定时间点执行某一些业务逻辑、间隔时间执行某一些业务逻辑等。我们在之前有讲过SpringBoot是已经集成了定时任务的，详见：第二十六章：SpringBoot使用@Scheduled创建定时任务，那么我们本章将会采用外置的quartz定时任务框架来完成定时任务的分布式单节点持久化，我们为什么要持久化定时任务呢？">
<meta property="og:locale" content="zh-CN">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/4461954-d80b777ca38632ba.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:updated_time" content="2018-01-23T15:50:33.231Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="第三十九章：基于SpringBoot &amp; Quartz完成定时任务分布式单节点持久化">
<meta name="twitter:description" content="定时任务在企业项目比较常用到，几乎所有的项目都会牵扯该功能模块，定时任务一般会处理指定时间点执行某一些业务逻辑、间隔时间执行某一些业务逻辑等。我们在之前有讲过SpringBoot是已经集成了定时任务的，详见：第二十六章：SpringBoot使用@Scheduled创建定时任务，那么我们本章将会采用外置的quartz定时任务框架来完成定时任务的分布式单节点持久化，我们为什么要持久化定时任务呢？">
<meta name="twitter:image" content="http://upload-images.jianshu.io/upload_images/4461954-d80b777ca38632ba.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
    

    
        <link rel="alternate" href="/" title="恒宇少年De成长之路" type="application/atom+xml" />
    

    

    <link rel="stylesheet" href="/libs/font-awesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="/libs/open-sans/styles.css">
    <link rel="stylesheet" href="/libs/source-code-pro/styles.css">

    <link rel="stylesheet" href="/css/style.css">

    <script src="/libs/jquery/2.1.3/jquery.min.js"></script>
    
    
        <link rel="stylesheet" href="/libs/lightgallery/css/lightgallery.min.css">
    
    
        <link rel="stylesheet" href="/libs/justified-gallery/justifiedGallery.min.css">
    
    
    
    


</head>

<body>
    <div id="container">
        <header id="header">
    <div id="header-main" class="header-inner">
        <div class="outer">
            <a href="/" id="logo">
                <i class="logo"></i>
                <span class="site-title">恒宇少年De成长之路</span>
            </a>
            <nav id="main-nav">
                
                    <a class="main-nav-link" href="/.">首页</a>
                
                    <a class="main-nav-link" href="/archives">时间轴</a>
                
                    <a class="main-nav-link" href="/categories">文章专题</a>
                
                    <a class="main-nav-link" href="/tags">标签云</a>
                
                    <a class="main-nav-link" href="/about">关于我</a>
                
            </nav>
            
                
                <nav id="sub-nav">
                    <div class="profile" id="profile-nav">
                        <a id="profile-anchor" href="javascript:;">
                            <img class="avatar" src="/css/images/avatar.png" />
                            <i class="fa fa-caret-down"></i>
                        </a>
                    </div>
                </nav>
            
            <div id="search-form-wrap">

    <form class="search-form">
        <input type="text" class="ins-search-input search-form-input" placeholder="搜索" />
        <!--button type="submit" class="search-form-submit"></button-->
    </form>
    <div class="ins-search">
    <div class="ins-search-mask"></div>
    <div class="ins-search-container">
        <div class="ins-input-wrapper">
            <input type="text" class="ins-search-input" placeholder="想要查找什么..." />
            <span class="ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
        </div>
        <div class="ins-section-wrapper">
            <div class="ins-section-container"></div>
        </div>
    </div>
</div>
<script>
(function (window) {
    var INSIGHT_CONFIG = {
        TRANSLATION: {
            POSTS: '文章',
            PAGES: '页面',
            CATEGORIES: '分类',
            TAGS: '标签',
            UNTITLED: '(未命名)',
        },
        ROOT_URL: '/',
        CONTENT_URL: '/content.json',
    };
    window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>
<script src="/js/insight.js"></script>

</div>
        </div>
    </div>
    <div id="main-nav-mobile" class="header-sub header-inner">
        <table class="menu outer">
            <tr>
                
                    <td ><a  class="main-nav-link" href="/.">首页</a></td>
                
                    <td ><a  class="main-nav-link" href="/archives">时间轴</a></td>
                
                    <td ><a  class="main-nav-link" href="/categories">文章专题</a></td>
                
                    <td ><a  class="main-nav-link" href="/tags">标签云</a></td>
                
                    <td ><a  class="main-nav-link" href="/about">关于我</a></td>
                
                <td>
                    
    <div class="search-form">
        <input type="text" class="ins-search-input search-form-input" placeholder="搜索" />
    </div>

                </td>
            </tr>
        </table>
    </div>
</header>

        <div class="outer">
            
                

<aside id="profile">
    <div class="inner profile-inner">
        <div class="base-info profile-block">
            <img id="avatar" src="/css/images/avatar.png" />
            <h2 id="name">恒宇少年</h2>
            <h3 id="title">Java软件工程师 &amp; 程序猿</h3>
            <span id="location"><i class="fa fa-map-marker"></i>山东, 济南</span>
            <a id="follow" target="_blank" href="https://www.jianshu.com/u/092df3f77bca">关注我</a>
        </div>
        <div class="article-info profile-block">
            <div class="article-info-block">
                43
                <span>文章</span>
            </div>
            <div class="article-info-block">
                2
                <span>标签</span>
            </div>
        </div>
        
        <div class="profile-block social-links">
            <table>
                <tr>
                    
                    
                    <td>
                        <a href="http://github.com/ppoffice/hexo-theme-icarus" target="_blank" title="github" class=tooltip>
                            <i class="fa fa-github"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="/" target="_blank" title="twitter" class=tooltip>
                            <i class="fa fa-twitter"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="/" target="_blank" title="facebook" class=tooltip>
                            <i class="fa fa-facebook"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="/" target="_blank" title="dribbble" class=tooltip>
                            <i class="fa fa-dribbble"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="/" target="_blank" title="rss" class=tooltip>
                            <i class="fa fa-rss"></i>
                        </a>
                    </td>
                    
                </tr>
            </table>
        </div>
        
    </div>
</aside>

            
            <section id="main"><article id="post-spring-boot-chapter-39" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 class="article-title" itemprop="name">
            第三十九章：基于SpringBoot &amp; Quartz完成定时任务分布式单节点持久化
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/spring-boot-chapter-39/">
            <time datetime="2017-11-04T16:00:00.000Z" itemprop="datePublished">2017-11-05</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/SpringBoot-核心技术/">SpringBoot 核心技术</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/SpringBoot/">SpringBoot</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>定时任务在企业项目比较常用到，几乎所有的项目都会牵扯该功能模块，定时任务一般会处理指定时间点执行某一些业务逻辑、间隔时间执行某一些业务逻辑等。我们在之前有讲过<code>SpringBoot</code>是已经集成了定时任务的，详见：<a href="http://www.jianshu.com/p/c7492aeb35a1" target="_blank" rel="noopener">第二十六章：SpringBoot使用@Scheduled创建定时任务</a>，那么我们本章将会采用外置的<code>quartz</code>定时任务框架来完成定时任务的分布式单节点持久化，我们为什么要持久化定时任务呢？<br><a id="more"></a><br>在一些项目中定时任务可能是必不可少的，由于某种特殊的原因定时任务可能丢失，如重启定时任务服务项目后，原内存中的定时任务就会被完全释放！那对于我们来说可能是致命的问题。当然也有强制的办法解决这类问题，但是如果我们把定时任务持久化到数据库，像维护普通逻辑数据那样维护任务，就会避免项目中遇到的种种的特殊情况。</p>
<h1 id="本章目标"><a href="#本章目标" class="headerlink" title="本章目标"></a>本章目标</h1><p>基于<code>SpringBoot</code>架构整合定时任务框架<code>quartz</code>来完成分布式单节点定时任务持久化，将任务持久化到数据库，更好的预防任务丢失。</p>
<h1 id="SpringBoot-企业级核心技术学习专题"><a href="#SpringBoot-企业级核心技术学习专题" class="headerlink" title="SpringBoot 企业级核心技术学习专题"></a>SpringBoot 企业级核心技术学习专题</h1><table>
<thead>
<tr>
<th style="text-align:center">专题</th>
<th style="text-align:left">专题名称</th>
<th style="text-align:left">专题描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">001</td>
<td style="text-align:left"><a href="http://www.jianshu.com/c/3f69deddbed3" target="_blank" rel="noopener">Spring Boot 核心技术</a></td>
<td style="text-align:left">讲解SpringBoot一些企业级层面的核心组件</td>
</tr>
<tr>
<td style="text-align:center">002</td>
<td style="text-align:left"><a href="https://gitee.com/hengboy/spring-boot-chapter" target="_blank" rel="noopener">Spring Boot 核心技术章节源码</a></td>
<td style="text-align:left">Spring Boot 核心技术简书每一篇文章码云对应源码</td>
</tr>
<tr>
<td style="text-align:center">003</td>
<td style="text-align:left"><a href="http://www.jianshu.com/c/1faac22666e7" target="_blank" rel="noopener">Spring Cloud 核心技术</a></td>
<td style="text-align:left">对Spring Cloud核心技术全面讲解</td>
</tr>
<tr>
<td style="text-align:center">004</td>
<td style="text-align:left"><a href="https://gitee.com/hengboy/spring-cloud-chapter" target="_blank" rel="noopener">Spring Cloud 核心技术章节源码</a></td>
<td style="text-align:left">Spring Cloud 核心技术简书每一篇文章对应源码</td>
</tr>
<tr>
<td style="text-align:center">005</td>
<td style="text-align:left"><a href="http://www.jianshu.com/c/ab4789177827" target="_blank" rel="noopener">QueryDSL 核心技术</a></td>
<td style="text-align:left">全面讲解QueryDSL核心技术以及基于SpringBoot整合SpringDataJPA</td>
</tr>
<tr>
<td style="text-align:center">006</td>
<td style="text-align:left"><a href="http://www.jianshu.com/c/f1b269bb2fd6" target="_blank" rel="noopener">SpringDataJPA 核心技术</a></td>
<td style="text-align:left">全面讲解SpringDataJPA核心技术</td>
</tr>
</tbody>
</table>
<h1 id="构建项目"><a href="#构建项目" class="headerlink" title="构建项目"></a>构建项目</h1><p>我们使用<code>idea</code>开发工具创建一个<code>SpringBoot</code>项目，pom.xml依赖配置如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line">...省略部分配置</span><br><span class="line">    &lt;properties&gt;</span><br><span class="line">    &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;</span><br><span class="line">    &lt;project.reporting.outputEncoding&gt;UTF-8&lt;/project.reporting.outputEncoding&gt;</span><br><span class="line">    &lt;java.version&gt;1.8&lt;/java.version&gt;</span><br><span class="line">    &lt;druid.version&gt;1.1.5&lt;/druid.version&gt;</span><br><span class="line">    &lt;quartz.version&gt;2.3.0&lt;/quartz.version&gt;</span><br><span class="line">  &lt;/properties&gt;</span><br><span class="line"></span><br><span class="line">  &lt;dependencies&gt;</span><br><span class="line">    &lt;!--spring data jpa相关--&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">    &lt;!--web相关依赖--&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">    &lt;!--数据库相关依赖--&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;mysql&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;</span><br><span class="line">      &lt;scope&gt;runtime&lt;/scope&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;com.alibaba&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;druid&lt;/artifactId&gt;</span><br><span class="line">      &lt;version&gt;$&#123;druid.version&#125;&lt;/version&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">    &lt;!--quartz相关依赖--&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;org.quartz-scheduler&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;quartz&lt;/artifactId&gt;</span><br><span class="line">      &lt;version&gt;$&#123;quartz.version&#125;&lt;/version&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;org.quartz-scheduler&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;quartz-jobs&lt;/artifactId&gt;</span><br><span class="line">      &lt;version&gt;$&#123;quartz.version&#125;&lt;/version&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">    &lt;!--定时任务需要依赖context模块--&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;org.springframework&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;spring-context-support&lt;/artifactId&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;lombok&lt;/artifactId&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;</span><br><span class="line">      &lt;scope&gt;test&lt;/scope&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">  &lt;/dependencies&gt;</span><br><span class="line">...省略部分配置</span><br></pre></td></tr></table></figure></p>
<p>我们采用的是<code>quartz</code>官方最新版本<code>2.3.0</code>，新版本的任务调度框架做出了很多封装，使用也变得简易明了。<br>创建初始化完成，下面我们来创建定时任务相关的<code>Configuration</code>配置。</p>
<h2 id="QuartzConfiguration"><a href="#QuartzConfiguration" class="headerlink" title="QuartzConfiguration"></a>QuartzConfiguration</h2><p><code>quartz</code>与<code>Spring</code>相关框架的整合方式有很多种，我们今天采用<code>jobDetail</code>使用<code>Spring Ioc</code>托管方式来完成整合，我们可以在定时任务实例中使用<code>Spring</code>注入注解完成业务逻辑处理，下面我先把全部的配置贴出来再逐步分析，配置类如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br></pre></td><td class="code"><pre><span class="line">package com.hengyu.chapter39.configuration;</span><br><span class="line"></span><br><span class="line">import org.quartz.spi.JobFactory;</span><br><span class="line">import org.quartz.spi.TriggerFiredBundle;</span><br><span class="line">import org.springframework.beans.factory.annotation.Autowire;</span><br><span class="line">import org.springframework.beans.factory.config.AutowireCapableBeanFactory;</span><br><span class="line">import org.springframework.context.ApplicationContext;</span><br><span class="line">import org.springframework.context.ApplicationContextAware;</span><br><span class="line">import org.springframework.context.annotation.Bean;</span><br><span class="line">import org.springframework.context.annotation.Configuration;</span><br><span class="line">import org.springframework.core.io.ClassPathResource;</span><br><span class="line">import org.springframework.scheduling.annotation.EnableScheduling;</span><br><span class="line">import org.springframework.scheduling.quartz.SchedulerFactoryBean;</span><br><span class="line">import org.springframework.scheduling.quartz.SpringBeanJobFactory;</span><br><span class="line"></span><br><span class="line">import javax.sql.DataSource;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * quartz定时任务配置</span><br><span class="line"> * ========================</span><br><span class="line"> * Created with IntelliJ IDEA.</span><br><span class="line"> * User：恒宇少年</span><br><span class="line"> * Date：2017/11/5</span><br><span class="line"> * Time：14:07</span><br><span class="line"> * 码云：http://git.oschina.net/jnyqy</span><br><span class="line"> * ========================</span><br><span class="line"> * @author  恒宇少年</span><br><span class="line"> */</span><br><span class="line">@Configuration</span><br><span class="line">@EnableScheduling</span><br><span class="line">public class QuartzConfiguration</span><br><span class="line">&#123;</span><br><span class="line">    /**</span><br><span class="line">     * 继承org.springframework.scheduling.quartz.SpringBeanJobFactory</span><br><span class="line">     * 实现任务实例化方式</span><br><span class="line">     */</span><br><span class="line">    public static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements</span><br><span class="line">            ApplicationContextAware &#123;</span><br><span class="line"></span><br><span class="line">        private transient AutowireCapableBeanFactory beanFactory;</span><br><span class="line"></span><br><span class="line">        @Override</span><br><span class="line">        public void setApplicationContext(final ApplicationContext context) &#123;</span><br><span class="line">            beanFactory = context.getAutowireCapableBeanFactory();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        /**</span><br><span class="line">         * 将job实例交给spring ioc托管</span><br><span class="line">         * 我们在job实例实现类内可以直接使用spring注入的调用被spring ioc管理的实例</span><br><span class="line">         * @param bundle</span><br><span class="line">         * @return</span><br><span class="line">         * @throws Exception</span><br><span class="line">         */</span><br><span class="line">        @Override</span><br><span class="line">        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception &#123;</span><br><span class="line">            final Object job = super.createJobInstance(bundle);</span><br><span class="line">            /**</span><br><span class="line">             * 将job实例交付给spring ioc</span><br><span class="line">             */</span><br><span class="line">            beanFactory.autowireBean(job);</span><br><span class="line">            return job;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 配置任务工厂实例</span><br><span class="line">     * @param applicationContext spring上下文实例</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    @Bean</span><br><span class="line">    public JobFactory jobFactory(ApplicationContext applicationContext)</span><br><span class="line">    &#123;</span><br><span class="line">        /**</span><br><span class="line">         * 采用自定义任务工厂 整合spring实例来完成构建任务</span><br><span class="line">         * see &#123;@link AutowiringSpringBeanJobFactory&#125;</span><br><span class="line">         */</span><br><span class="line">        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();</span><br><span class="line">        jobFactory.setApplicationContext(applicationContext);</span><br><span class="line">        return jobFactory;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 配置任务调度器</span><br><span class="line">     * 使用项目数据源作为quartz数据源</span><br><span class="line">     * @param jobFactory 自定义配置任务工厂</span><br><span class="line">     * @param dataSource 数据源实例</span><br><span class="line">     * @return</span><br><span class="line">     * @throws Exception</span><br><span class="line">     */</span><br><span class="line">    @Bean(destroyMethod = &quot;destroy&quot;,autowire = Autowire.NO)</span><br><span class="line">    public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, DataSource dataSource) throws Exception</span><br><span class="line">    &#123;</span><br><span class="line">        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();</span><br><span class="line">        //将spring管理job自定义工厂交由调度器维护</span><br><span class="line">        schedulerFactoryBean.setJobFactory(jobFactory);</span><br><span class="line">        //设置覆盖已存在的任务</span><br><span class="line">        schedulerFactoryBean.setOverwriteExistingJobs(true);</span><br><span class="line">        //项目启动完成后，等待2秒后开始执行调度器初始化</span><br><span class="line">        schedulerFactoryBean.setStartupDelay(2);</span><br><span class="line">        //设置调度器自动运行</span><br><span class="line">        schedulerFactoryBean.setAutoStartup(true);</span><br><span class="line">        //设置数据源，使用与项目统一数据源</span><br><span class="line">        schedulerFactoryBean.setDataSource(dataSource);</span><br><span class="line">        //设置上下文spring bean name</span><br><span class="line">        schedulerFactoryBean.setApplicationContextSchedulerContextKey(&quot;applicationContext&quot;);</span><br><span class="line">        //设置配置文件位置</span><br><span class="line">        schedulerFactoryBean.setConfigLocation(new ClassPathResource(&quot;/quartz.properties&quot;));</span><br><span class="line">        return schedulerFactoryBean;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="AutowiringSpringBeanJobFactory"><a href="#AutowiringSpringBeanJobFactory" class="headerlink" title="AutowiringSpringBeanJobFactory"></a>AutowiringSpringBeanJobFactory</h3><p>可以看到上面配置类中，<code>AutowiringSpringBeanJobFactory</code>我们继承了<code>SpringBeanJobFactory</code>类，并且通过实现<code>ApplicationContextAware</code>接口获取<code>ApplicationContext</code>设置方法，通过外部实例化时设置<code>ApplicationContext</code>实例对象，在<code>createJobInstance</code>方法内，我们采用<code>AutowireCapableBeanFactory</code>来托管<code>SpringBeanJobFactory</code>类中<code>createJobInstance</code>方法返回的定时任务实例，这样我们就可以在定时任务类内使用<code>Spring Ioc</code>相关的注解进行注入业务逻辑实例了。</p>
<h3 id="JobFactory"><a href="#JobFactory" class="headerlink" title="JobFactory"></a>JobFactory</h3><p>任务工厂是在本章配置调度器时所需要的实例，我们通过<code>jobFactory</code>方法注入<code>ApplicationContext</code>实例，来创建一个<code>AutowiringSpringBeanJobFactory</code>对象，并且将对象实例托管到<code>Spring Ioc</code>容器内。</p>
<h3 id="SchedulerFactoryBean"><a href="#SchedulerFactoryBean" class="headerlink" title="SchedulerFactoryBean"></a>SchedulerFactoryBean</h3><p>我们本章采用的是项目内部数据源的方式来设置调度器的<code>jobSotre</code>，官方<code>quartz</code>有两种持久化的配置方案。</p>
<p>第一种：采用<code>quartz.properties</code>配置文件配置独立的定时任务数据源，可以与使用项目的数据库完全独立。<br>第二种：采用与创建项目统一个数据源，定时任务持久化相关的表与业务逻辑在同一个数据库内。</p>
<p>可以根据实际的项目需求采取不同的方案，我们本章主要是通过第二种方案来进行讲解，在上面配置类中可以看到方法<code>schedulerFactoryBean</code>内自动注入了<code>JobFactory</code>实例，也就是我们自定义的<code>AutowiringSpringBeanJobFactory</code>任务工厂实例，另外一个参数就是<code>DataSource</code>，在我们引入<code>spring-starter-data-jpa</code>依赖后会根据<code>application.yml</code>文件内的数据源相关配置自动实例化<code>DataSource</code>实例，这里直接注入是没有问题的。</p>
<p>我们通过调用<code>SchedulerFactoryBean</code>对象的<code>setConfigLocation</code>方法来设置<code>quartz</code>定时任务框架的基本配置，配置文件所在位置：<code>resources/quartz.properties</code> =&gt; <code>classpath:/quartz.properties</code>下。</p>
<blockquote>
<p>注意：quartz.properties配置文件一定要放在<code>classpath</code>下，放在别的位置有部分功能不会生效。</p>
</blockquote>
<p>下面我们来看下<code>quartz.properties</code>文件内的配置，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">#调度器实例名称</span><br><span class="line">org.quartz.scheduler.instanceName = quartzScheduler</span><br><span class="line"></span><br><span class="line">#调度器实例编号自动生成</span><br><span class="line">org.quartz.scheduler.instanceId = AUTO</span><br><span class="line"></span><br><span class="line">#持久化方式配置</span><br><span class="line">org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX</span><br><span class="line"></span><br><span class="line">#持久化方式配置数据驱动，MySQL数据库</span><br><span class="line">org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate</span><br><span class="line"></span><br><span class="line">#quartz相关数据表前缀名</span><br><span class="line">org.quartz.jobStore.tablePrefix = QRTZ_</span><br><span class="line"></span><br><span class="line">#开启分布式部署</span><br><span class="line">org.quartz.jobStore.isClustered = true</span><br><span class="line">#配置是否使用</span><br><span class="line">org.quartz.jobStore.useProperties = false</span><br><span class="line"></span><br><span class="line">#分布式节点有效性检查时间间隔，单位：毫秒</span><br><span class="line">org.quartz.jobStore.clusterCheckinInterval = 20000</span><br><span class="line"></span><br><span class="line">#线程池实现类</span><br><span class="line">org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool</span><br><span class="line"></span><br><span class="line">#执行最大并发线程数量</span><br><span class="line">org.quartz.threadPool.threadCount = 10</span><br><span class="line"></span><br><span class="line">#线程优先级</span><br><span class="line">org.quartz.threadPool.threadPriority = 5</span><br><span class="line"></span><br><span class="line">#配置为守护线程，设置后任务将不会执行</span><br><span class="line">#org.quartz.threadPool.makeThreadsDaemons=true</span><br><span class="line"></span><br><span class="line">#配置是否启动自动加载数据库内的定时任务，默认true</span><br><span class="line">org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true</span><br></pre></td></tr></table></figure></p>
<p>由于我们下一章需要做分布式多节点自动交付高可用，本章的配置文件加入了分布式相关的配置。<br>在上面配置中<code>org.quartz.jobStore.class</code>与<code>org.quartz.jobStore.driverDelegateClass</code>是定时任务持久化的关键配置，配置了数据库持久化定时任务以及采用<code>MySQL</code>数据库进行连接，当然这里我们也可以配置其他的数据库，如下所示：<br><code>PostgreSQL</code> ： <code>org.quartz.impl.jdbcjobstore.PostgreSQLDelegate</code><br><code>Sybase</code> : <code>org.quartz.impl.jdbcjobstore.SybaseDelegate</code><br><code>MSSQL</code> : <code>org.quartz.impl.jdbcjobstore.MSSQLDelegate</code><br><code>HSQLDB</code> : <code>org.quartz.impl.jdbcjobstore.HSQLDBDelegate</code><br><code>Oracle</code> : <code>org.quartz.impl.jdbcjobstore.oracle.OracleDelegate</code></p>
<p><code>org.quartz.jobStore.tablePrefix</code>属性配置了定时任务数据表的前缀，在<code>quartz</code>官方提供的创建表<code>SQL脚本</code>默认就是<code>qrtz_</code>，在对应的<code>XxxDelegate驱动类内</code>也是使用的默认值，所以这里我们如果修改表名前缀，配置可以去掉。</p>
<p><code>org.quartz.jobStore.isClustered</code>属性配置了开启定时任务分布式功能，再开启分布式时对应属性<code>org.quartz.scheduler.instanceId</code> 改成<code>Auto</code>配置即可，实例唯一标识会自动生成，这个标识具体生成的内容，我们一会在运行的控制台就可以看到了，定时任务分布式准备好后会输出相关的分布式节点配置信息。</p>
<p>创建表SQL会在本章源码<code>resources</code>目录下，源码地址<a href="https://gitee.com/hengboy/spring-boot-chapter" target="_blank" rel="noopener">https://gitee.com/hengboy/spring-boot-chapter</a>。</p>
<h2 id="准备测试"><a href="#准备测试" class="headerlink" title="准备测试"></a>准备测试</h2><p>我们先来创建一个简单的商品数据表，建表<code>SQL</code>如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">DROP TABLE IF EXISTS `basic_good_info`;</span><br><span class="line">CREATE TABLE `basic_good_info` (</span><br><span class="line">  `BGI_ID` int(11) NOT NULL AUTO_INCREMENT COMMENT &apos;商品编号&apos;,</span><br><span class="line">  `BGI_NAME` varchar(20) DEFAULT NULL COMMENT &apos;商品名称&apos;,</span><br><span class="line">  `BGI_PRICE` decimal(8,2) DEFAULT NULL COMMENT &apos;单价&apos;,</span><br><span class="line">  `BGI_UNIT` varchar(10) DEFAULT NULL COMMENT &apos;单位&apos;,</span><br><span class="line">  PRIMARY KEY (`BGI_ID`)</span><br><span class="line">) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT=&apos;商品基本信息&apos;;</span><br></pre></td></tr></table></figure></p>
<h3 id="GoodEntity"><a href="#GoodEntity" class="headerlink" title="GoodEntity"></a>GoodEntity</h3><p>我们先来针对表<code>basic_good_info</code>创建一个实体，并且添加<code>JPA</code>相关的配置，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">package com.hengyu.chapter39.good.entity;</span><br><span class="line"></span><br><span class="line">import lombok.Data;</span><br><span class="line"></span><br><span class="line">import javax.persistence.*;</span><br><span class="line">import java.math.BigDecimal;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * ========================</span><br><span class="line"> *</span><br><span class="line"> * @author 恒宇少年</span><br><span class="line"> * Created with IntelliJ IDEA.</span><br><span class="line"> * Date：2017/11/5</span><br><span class="line"> * Time：14:59</span><br><span class="line"> * 码云：http://git.oschina.net/jnyqy</span><br><span class="line"> * ========================</span><br><span class="line"> */</span><br><span class="line">@Entity</span><br><span class="line">@Table(name = &quot;basic_good_info&quot;)</span><br><span class="line">@Data</span><br><span class="line">public class GoodInfoEntity</span><br><span class="line">&#123;</span><br><span class="line">    /**</span><br><span class="line">     * 商品编号</span><br><span class="line">     */</span><br><span class="line">    @Id</span><br><span class="line">    @GeneratedValue</span><br><span class="line">    @Column(name = &quot;bgi_id&quot;)</span><br><span class="line">    private Long id;</span><br><span class="line">    /**</span><br><span class="line">     * 商品名称</span><br><span class="line">     */</span><br><span class="line">    @Column(name = &quot;bgi_name&quot;)</span><br><span class="line">    private String name;</span><br><span class="line">    /**</span><br><span class="line">     * 商品单位</span><br><span class="line">     */</span><br><span class="line">    @Column(name = &quot;bgi_unit&quot;)</span><br><span class="line">    private String unit;</span><br><span class="line">    /**</span><br><span class="line">     * 商品单价</span><br><span class="line">     */</span><br><span class="line">    @Column(name = &quot;bgi_price&quot;)</span><br><span class="line">    private BigDecimal price;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>下面我们根据商品实体来创建<code>JPA</code>接口，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * ========================</span><br><span class="line"> * Created with IntelliJ IDEA.</span><br><span class="line"> * Date：2017/11/5</span><br><span class="line"> * Time：14:55</span><br><span class="line"> * 码云：http://git.oschina.net/jnyqy</span><br><span class="line"> * ========================</span><br><span class="line"> * @author 恒宇少年</span><br><span class="line"> */</span><br><span class="line">public interface GoodInfoRepository</span><br><span class="line">    extends JpaRepository&lt;GoodInfoEntity,Long&gt;</span><br><span class="line">&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>接下来我们再来添加一个商品添加的控制器方法，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * ========================</span><br><span class="line"> *</span><br><span class="line"> * @author 恒宇少年</span><br><span class="line"> * Created with IntelliJ IDEA.</span><br><span class="line"> * Date：2017/11/5</span><br><span class="line"> * Time：15:02</span><br><span class="line"> * 码云：http://git.oschina.net/jnyqy</span><br><span class="line"> * ========================</span><br><span class="line"> */</span><br><span class="line">@RestController</span><br><span class="line">@RequestMapping(value = &quot;/good&quot;)</span><br><span class="line">public class GoodController</span><br><span class="line">&#123;</span><br><span class="line">    /**</span><br><span class="line">     * 商品业务逻辑实现</span><br><span class="line">     */</span><br><span class="line">    @Autowired</span><br><span class="line">    private GoodInfoService goodInfoService;</span><br><span class="line">    /**</span><br><span class="line">     * 添加商品</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    @RequestMapping(value = &quot;/save&quot;)</span><br><span class="line">    public Long save(GoodInfoEntity good) throws Exception</span><br><span class="line">    &#123;</span><br><span class="line">        return goodInfoService.saveGood(good);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>在请求商品添加方法时，我们调用了<code>GoodInfoService</code>内的<code>saveGood</code>方法，传递一个商品的实例作为参数。我们接下来看看该类内相关代码，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 商品业务逻辑</span><br><span class="line"> * ========================</span><br><span class="line"> *</span><br><span class="line"> * @author 恒宇少年</span><br><span class="line"> * Created with IntelliJ IDEA.</span><br><span class="line"> * Date：2017/11/5</span><br><span class="line"> * Time：15:04</span><br><span class="line"> * 码云：http://git.oschina.net/jnyqy</span><br><span class="line"> * ========================</span><br><span class="line"> */</span><br><span class="line">@Service</span><br><span class="line">@Transactional(rollbackFor = Exception.class)</span><br><span class="line">public class GoodInfoService</span><br><span class="line">&#123;</span><br><span class="line">    /**</span><br><span class="line">     * 注入任务调度器</span><br><span class="line">     */</span><br><span class="line">    @Autowired</span><br><span class="line">    private Scheduler scheduler;</span><br><span class="line">    /**</span><br><span class="line">     * 商品数据接口</span><br><span class="line">     */</span><br><span class="line">    @Autowired</span><br><span class="line">    private GoodInfoRepository goodInfoRepository;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 保存商品基本信息</span><br><span class="line">     * @param good 商品实例</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    public Long saveGood(GoodInfoEntity good) throws Exception</span><br><span class="line">    &#123;</span><br><span class="line">        goodInfoRepository.save(good);</span><br><span class="line">        return good.getId();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>我们只是作为保存商品的操作，下面我们来模拟一个需求，在商品添加完成后1分钟我们通知后续的逻辑进行下一步处理，同时开始商品库存定时检查的任务。</p>
<h3 id="定义商品添加定时任务"><a href="#定义商品添加定时任务" class="headerlink" title="定义商品添加定时任务"></a>定义商品添加定时任务</h3><p>我们先来创建一个任务实例，并且继承<code>org.springframework.scheduling.quartz.QuartzJobBean</code>抽象类，重写父抽象类内的<code>executeInternal</code>方法来实现任务的主体逻辑。如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 商品添加定时任务实现类</span><br><span class="line"> * ========================</span><br><span class="line"> * Created with IntelliJ IDEA.</span><br><span class="line"> * User：恒宇少年</span><br><span class="line"> * Date：2017/11/5</span><br><span class="line"> * Time：14:47</span><br><span class="line"> * 码云：http://git.oschina.net/jnyqy</span><br><span class="line"> * ========================</span><br><span class="line"> * @author 恒宇少年</span><br><span class="line"> */</span><br><span class="line">public class GoodAddTimer</span><br><span class="line">    extends QuartzJobBean</span><br><span class="line">&#123;</span><br><span class="line">    /**</span><br><span class="line">     * logback</span><br><span class="line">     */</span><br><span class="line">    static Logger logger = LoggerFactory.getLogger(GoodAddTimer.class);</span><br><span class="line">    /**</span><br><span class="line">     * 定时任务逻辑实现方法</span><br><span class="line">     * 每当触发器触发时会执行该方法逻辑</span><br><span class="line">     * @param jobExecutionContext 任务执行上下文</span><br><span class="line">     * @throws JobExecutionException</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException &#123;</span><br><span class="line">        logger.info(&quot;商品添加完成后执行任务，任务时间：&#123;&#125;&quot;,new Date());</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>在任务主体逻辑内，我们只是做了一个简单的输出任务执行的时间，下面我们再来创建库存定时检查任务。</p>
<h3 id="定义商品库存检查任务"><a href="#定义商品库存检查任务" class="headerlink" title="定义商品库存检查任务"></a>定义商品库存检查任务</h3><p>同样需要继承<code>org.springframework.scheduling.quartz.QuartzJobBean</code>抽象类实现抽象类内的<code>executeInternal</code>方法，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 商品库存检查定时任务</span><br><span class="line"> * ========================</span><br><span class="line"> *</span><br><span class="line"> * @author 恒宇少年</span><br><span class="line"> * Created with IntelliJ IDEA.</span><br><span class="line"> * Date：2017/11/5</span><br><span class="line"> * Time：15:47</span><br><span class="line"> * 码云：http://git.oschina.net/jnyqy</span><br><span class="line"> * ========================</span><br><span class="line"> */</span><br><span class="line">public class GoodStockCheckTimer</span><br><span class="line">    extends QuartzJobBean</span><br><span class="line">&#123;</span><br><span class="line">    /**</span><br><span class="line">     * logback</span><br><span class="line">     */</span><br><span class="line">    static Logger logger = LoggerFactory.getLogger(GoodStockCheckTimer.class);</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException &#123;</span><br><span class="line">        logger.info(&quot;执行库存检查定时任务，执行时间：&#123;&#125;&quot;,new Date());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>都是简单的做了下日志的输出，下面我们需要重构<code>GoodInfoService</code>内的<code>saveGood</code>方法，对应的添加上面两个任务的创建。</p>
<h3 id="设置商品添加任务到调度器"><a href="#设置商品添加任务到调度器" class="headerlink" title="设置商品添加任务到调度器"></a>设置商品添加任务到调度器</h3><p>在<code>GoodInfoService</code>类内添加<code>buildCreateGoodTimer</code>方法用于实例化商品添加任务，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line">     * 构建创建商品定时任务</span><br><span class="line">     */</span><br><span class="line">    public void buildCreateGoodTimer() throws Exception</span><br><span class="line">    &#123;</span><br><span class="line">        //设置开始时间为1分钟后</span><br><span class="line">        long startAtTime = System.currentTimeMillis() + 1000 * 60;</span><br><span class="line">        //任务名称</span><br><span class="line">        String name = UUID.randomUUID().toString();</span><br><span class="line">        //任务所属分组</span><br><span class="line">        String group = GoodAddTimer.class.getName();</span><br><span class="line">        //创建任务</span><br><span class="line">        JobDetail jobDetail = JobBuilder.newJob(GoodAddTimer.class).withIdentity(name,group).build();</span><br><span class="line">        //创建任务触发器</span><br><span class="line">        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(name,group).startAt(new Date(startAtTime)).build();</span><br><span class="line">        //将触发器与任务绑定到调度器内</span><br><span class="line">        scheduler.scheduleJob(jobDetail, trigger);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>在上面方法中我们定义的<code>GoodAddTimer</code>实例只运行一次，在商品添加完成后延迟1分钟进行调用任务主体逻辑。</p>
<blockquote>
<p>其中任务的名称以及任务的分组是为了区分任务做的限制，在同一个分组下如果加入同样名称的任务，则会提示任务已经存在，添加失败的提示。</p>
</blockquote>
<p>我们通过<code>JobDetail</code>来构建一个任务实例，设置<code>GoodAddTimer</code>类作为任务运行目标对象，当任务被触发时就会执行<code>GoodAddTimer</code>内的<code>executeInternal</code>方法。</p>
<p>一个任务需要设置对应的触发器，触发器也分为很多种，该任务中我们并没有采用<code>cron</code>表达式来设置触发器，而是调用<code>startAt</code>方法设置任务开始执行时间。</p>
<p>最后将任务以及任务的触发器共同交付给任务调度器，这样就完成了一个任务的设置。</p>
<h3 id="设置商品库存检查到任务调度器"><a href="#设置商品库存检查到任务调度器" class="headerlink" title="设置商品库存检查到任务调度器"></a>设置商品库存检查到任务调度器</h3><p>在<code>GoodInfoService</code>类内添加<code>buildGoodStockTimer</code>方法用于实例化商品添加任务，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line">     * 构建商品库存定时任务</span><br><span class="line">     * @throws Exception</span><br><span class="line">     */</span><br><span class="line">    public void buildGoodStockTimer() throws Exception</span><br><span class="line">    &#123;</span><br><span class="line">        //任务名称</span><br><span class="line">        String name = UUID.randomUUID().toString();</span><br><span class="line">        //任务所属分组</span><br><span class="line">        String group = GoodStockCheckTimer.class.getName();</span><br><span class="line"></span><br><span class="line">        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(&quot;0/30 * * * * ?&quot;);</span><br><span class="line">        //创建任务</span><br><span class="line">        JobDetail jobDetail = JobBuilder.newJob(GoodStockCheckTimer.class).withIdentity(name,group).build();</span><br><span class="line">        //创建任务触发器</span><br><span class="line">        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(name,group).withSchedule(scheduleBuilder).build();</span><br><span class="line">        //将触发器与任务绑定到调度器内</span><br><span class="line">        scheduler.scheduleJob(jobDetail, trigger);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>该任务的触发器我们采用了<code>cron</code>表达式来设置，每隔30秒执行一次任务主体逻辑。</p>
<blockquote>
<p>任务触发器在创建时<code>cron</code>表达式可以搭配<code>startAt</code>方法来同时使用。</p>
</blockquote>
<p>下面我们修改<code>GoodInfoService</code>内的<code>saveGood</code>方法，分别调用设置任务的两个方法，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line">     * 保存商品基本信息</span><br><span class="line">     * @param good 商品实例</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    public Long saveGood(GoodInfoEntity good) throws Exception</span><br><span class="line">    &#123;</span><br><span class="line">        goodInfoRepository.save(good);</span><br><span class="line">        //构建创建商品定时任务</span><br><span class="line">        buildCreateGoodTimer();</span><br><span class="line">        //构建商品库存定时任务</span><br><span class="line">        buildGoodStockTimer();</span><br><span class="line">        return good.getId();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>下面我们就来测试下任务是否可以顺序的被持久化到数据库，并且是否可以在重启服务后执行重启前添加的任务。</p>
<h1 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h1><p><code>下面我们来启动项目，启动成功后</code>，我们来查看控制台输出的分布式节点的信息，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">2017-11-05 18:09:40.052  INFO 7708 --- [           main] c.hengyu.chapter39.Chapter39Application  : 【【【【【【定时任务分布式节点 - 1 已启动】】】】】】</span><br><span class="line">2017-11-05 18:09:42.005  INFO 7708 --- [lerFactoryBean]] o.s.s.quartz.SchedulerFactoryBean        : Starting Quartz Scheduler now, after delay of 2 seconds</span><br><span class="line">2017-11-05 18:09:42.027  INFO 7708 --- [lerFactoryBean]] o.s.s.quartz.LocalDataSourceJobStore     : ClusterManager: detected 1 failed or restarted instances.</span><br><span class="line">2017-11-05 18:09:42.027  INFO 7708 --- [lerFactoryBean]] o.s.s.quartz.LocalDataSourceJobStore     : ClusterManager: Scanning for instance &quot;yuqiyu1509876084785&quot;&apos;s failed in-progress jobs.</span><br><span class="line">2017-11-05 18:09:42.031  INFO 7708 --- [lerFactoryBean]] o.s.s.quartz.LocalDataSourceJobStore     : ClusterManager: ......Freed 1 acquired trigger(s).</span><br><span class="line">2017-11-05 18:09:42.033  INFO 7708 --- [lerFactoryBean]] org.quartz.core.QuartzScheduler          : Scheduler schedulerFactoryBean_$_yuqiyu1509876579404 started.</span><br></pre></td></tr></table></figure></p>
<p>定时任务是在项目启动后2秒进行执行初始化，并且通过<code>ClusterManager</code>来完成了<code>instance</code>的创建，创建的节点唯一标识为<code>yuqiyu1509876084785</code>。</p>
<p>编写商品控制器请求方法测试用例，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">@RunWith(SpringRunner.class)</span><br><span class="line">@SpringBootTest</span><br><span class="line">public class Chapter39ApplicationTests &#123;</span><br><span class="line">  /**</span><br><span class="line">   * 模拟mvc测试对象</span><br><span class="line">   */</span><br><span class="line">  private MockMvc mockMvc;</span><br><span class="line"></span><br><span class="line">  /**</span><br><span class="line">   * web项目上下文</span><br><span class="line">   */</span><br><span class="line">  @Autowired</span><br><span class="line">  private WebApplicationContext webApplicationContext;</span><br><span class="line"></span><br><span class="line">  /**</span><br><span class="line">   * 所有测试方法执行之前执行该方法</span><br><span class="line">   */</span><br><span class="line">  @Before</span><br><span class="line">  public void before() &#123;</span><br><span class="line">    //获取mockmvc对象实例</span><br><span class="line">    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  /**</span><br><span class="line">   * 测试添加商品</span><br><span class="line">   * @throws Exception</span><br><span class="line">   */</span><br><span class="line">  @Test</span><br><span class="line">  public void addGood() throws Exception</span><br><span class="line">  &#123;</span><br><span class="line">    MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(&quot;/good/save&quot;)</span><br><span class="line">        .param(&quot;name&quot;,&quot;西瓜&quot;)</span><br><span class="line">        .param(&quot;unit&quot;,&quot;斤&quot;)</span><br><span class="line">        .param(&quot;price&quot;,&quot;12.88&quot;)</span><br><span class="line">    )</span><br><span class="line">        .andDo(MockMvcResultHandlers.print())</span><br><span class="line">        .andExpect(MockMvcResultMatchers.status().is(200))</span><br><span class="line">        .andReturn();</span><br><span class="line">    result.getResponse().setCharacterEncoding(&quot;UTF-8&quot;);</span><br><span class="line">        System.out.println(result.getResponse().getContentAsString());</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></p>
<p>测试用例相关文章请访问<a href="http://www.jianshu.com/p/d8f844711bf4" target="_blank" rel="noopener">第三十五章：SpringBoot与单元测试的小秘密</a>，我们来执行<code>addGood</code>测试方法，查看控制台输出，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">....省略部分输出</span><br><span class="line">Hibernate: insert into basic_good_info (bgi_name, bgi_price, bgi_unit) values (?, ?, ?)</span><br><span class="line">2017-11-05 18:06:35.699 TRACE 7560 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [西瓜]</span><br><span class="line">2017-11-05 18:06:35.701 TRACE 7560 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [NUMERIC] - [12.88]</span><br><span class="line">2017-11-05 18:06:35.701 TRACE 7560 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [VARCHAR] - [斤]</span><br><span class="line">....省略部分输出</span><br><span class="line">8</span><br><span class="line">....省略部分输出</span><br></pre></td></tr></table></figure></p>
<p>可以看到我们的商品已被成功的写入到数据库并且输出的主键值，我们的任务是否也成功的被写入到数据库了呢？我们来查看<code>qrtz_job_details</code>表内任务列表，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">schedulerFactoryBean  7567c9d7-76f5-47f3-bc5d-b934f4c1063b  com.hengyu.chapter39.timers.GoodStockCheckTimer   com.hengyu.chapter39.timers.GoodStockCheckTimer 0 0 0 0 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787000737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F40000000000010770800000010000000007800</span><br><span class="line">schedulerFactoryBean  e5e08ab0-9be3-43fb-93b8-b9490432a5d7  com.hengyu.chapter39.timers.GoodAddTimer    com.hengyu.chapter39.timers.GoodAddTimer  0 0 0 0 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787000737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F40000000000010770800000010000000007800</span><br></pre></td></tr></table></figure></p>
<p>任务已经被成功的持久化到数据库内，等待1分钟后查看控制台输出内容如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">2017-11-05 18:12:30.017  INFO 7708 --- [ryBean_Worker-1] c.h.c.timers.GoodStockCheckTimer         : 执行库存检查定时任务，执行时间：Sun Nov 05 18:12:30 CST 2017</span><br><span class="line">2017-11-05 18:13:00.009  INFO 7708 --- [ryBean_Worker-2] c.h.c.timers.GoodStockCheckTimer         : 执行库存检查定时任务，执行时间：Sun Nov 05 18:13:00 CST 2017</span><br><span class="line">2017-11-05 18:13:02.090  INFO 7708 --- [ryBean_Worker-3] c.hengyu.chapter39.timers.GoodAddTimer   : 商品添加完成后执行任务，任务时间：Sun Nov 05 18:13:02 CST 2017</span><br></pre></td></tr></table></figure></p>
<p>根据输出的内容来判定完全吻合我们的配置参数，库存检查为30秒执行一次，而添加成功后的提醒则是1分钟后执行一次。执行完成后就会被直接销毁，我们再来查看数据库表<code>qrtz_job_details</code>，这时就可以看到还剩下<code>1个任务</code>。</p>
<h4 id="重启服务任务是否自动执行？"><a href="#重启服务任务是否自动执行？" class="headerlink" title="重启服务任务是否自动执行？"></a>重启服务任务是否自动执行？</h4><p>下面我们把项目重启下，然后观察控制台的输出内容，如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">2017-11-05 18:15:54.018  INFO 7536 --- [           main] c.hengyu.chapter39.Chapter39Application  : 【【【【【【定时任务分布式节点 - 1 已启动】】】】】】</span><br><span class="line">2017-11-05 18:15:55.975  INFO 7536 --- [lerFactoryBean]] o.s.s.quartz.SchedulerFactoryBean        : Starting Quartz Scheduler now, after delay of 2 seconds</span><br><span class="line">2017-11-05 18:15:56.000  INFO 7536 --- [lerFactoryBean]] org.quartz.core.QuartzScheduler          : Scheduler schedulerFactoryBean_$_yuqiyu1509876953202 started.</span><br><span class="line">2017-11-05 18:16:15.999  INFO 7536 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore     : ClusterManager: detected 1 failed or restarted instances.</span><br><span class="line">2017-11-05 18:16:16.000  INFO 7536 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore     : ClusterManager: Scanning for instance &quot;yuqiyu1509876579404&quot;&apos;s failed in-progress jobs.</span><br><span class="line">2017-11-05 18:16:16.005  INFO 7536 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore     : ClusterManager: ......Freed 1 acquired trigger(s).</span><br><span class="line">2017-11-05 18:16:16.041  INFO 7536 --- [ryBean_Worker-1] c.h.c.timers.GoodStockCheckTimer         : 执行库存检查定时任务，执行时间：Sun Nov 05 18:16:16 CST 2017</span><br></pre></td></tr></table></figure></p>
<p>可以看到成功的自动执行了我们在重启之前配置的任务。</p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>本章主要讲解了<code>SpringBoot</code>整合<code>quartz</code>定时任务框架，完成了分布式单节点任务持久化，下一章我们会讲解任务参数传递以及分布式多节点任务自动负载。</p>
<p>本章源码已经上传到码云：<br>SpringBoot配套源码地址：<a href="https://gitee.com/hengboy/spring-boot-chapter" target="_blank" rel="noopener">https://gitee.com/hengboy/spring-boot-chapter</a><br>SpringCloud配套源码地址：<a href="https://gitee.com/hengboy/spring-cloud-chapter" target="_blank" rel="noopener">https://gitee.com/hengboy/spring-cloud-chapter</a><br>SpringBoot相关系列文章请访问：<a href="http://www.jianshu.com/p/9a08417e4e84" target="_blank" rel="noopener">目录：SpringBoot学习目录</a><br>QueryDSL相关系列文章请访问：<a href="http://www.jianshu.com/p/99a5ec5c3bd5" target="_blank" rel="noopener">QueryDSL通用查询框架学习目录</a><br>SpringDataJPA相关系列文章请访问：<a href="http://www.jianshu.com/p/615ed9c1fe84" target="_blank" rel="noopener">目录：SpringDataJPA学习目录</a><br>SpringBoot相关文章请访问：<a href="http://www.jianshu.com/p/9a08417e4e84" target="_blank" rel="noopener">目录：SpringBoot学习目录</a>，感谢阅读！<br>欢迎加入QQ技术交流群，共同进步。<br><img src="http://upload-images.jianshu.io/upload_images/4461954-d80b777ca38632ba.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="">QQ技术交流群</p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    

        </footer>
    </div>
    
        
<nav id="article-nav">
    
        <a href="/spring-boot-chapter-40/" id="article-nav-newer" class="article-nav-link-wrap">
            <strong class="article-nav-caption">上一篇</strong>
            <div class="article-nav-title">
                
                    第四十章：基于SpringBoot &amp; Quartz完成定时任务分布式多节点负载持久化
                
            </div>
        </a>
    
    
        <a href="/spring-boot-chapter-38/" id="article-nav-older" class="article-nav-link-wrap">
            <strong class="article-nav-caption">下一篇</strong>
            <div class="article-nav-title">第三十八章：基于SpringBoot架构使用Profile完成打包环境分离</div>
        </a>
    
</nav>


    
</article>


    
    

</section>
            
			
				
<aside id="sidebar">
   
        
    <div class="widget-wrap">
        <h3 class="widget-title">最新文章</h3>
        <div class="widget">
            <ul id="recent-post" class="no-thumbnail">
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/SpringBoot-核心技术/">SpringBoot 核心技术</a></p>
                            <p class="item-title"><a href="/spring-boot-chapter-52/" class="title">第五十二章：基于SpringBoot2使用Rest访问MongoDB数据</a></p>
                            <p class="item-date"><time datetime="2018-04-21T16:00:00.000Z" itemprop="datePublished">2018-04-22</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/SpringBoot-核心技术/">SpringBoot 核心技术</a></p>
                            <p class="item-title"><a href="/spring-boot-chapter-51/" class="title">第五十一章：基于SpringBoot2 &amp; MongoDB完成自动化集成</a></p>
                            <p class="item-date"><time datetime="2018-04-15T16:00:00.000Z" itemprop="datePublished">2018-04-16</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/SpringBoot-核心技术/">SpringBoot 核心技术</a></p>
                            <p class="item-title"><a href="/spring-boot-chapter-50/" class="title">第五十章：SpringBoot2.0新特性 - 岂止至今最简单redis缓存集成</a></p>
                            <p class="item-date"><time datetime="2018-04-14T16:00:00.000Z" itemprop="datePublished">2018-04-15</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/SpringBoot-核心技术/">SpringBoot 核心技术</a></p>
                            <p class="item-title"><a href="/spring-boot-chapter-49/" class="title">第四十九章：SpringBoot2.0新特性 - 你get到WebMvcConfigurer两种配置方式了吗？</a></p>
                            <p class="item-date"><time datetime="2018-03-16T16:00:00.000Z" itemprop="datePublished">2018-03-17</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/SpringBoot-核心技术/">SpringBoot 核心技术</a></p>
                            <p class="item-title"><a href="/spring-boot-chapter-48/" class="title">第四十八章：SpringBoot2.0新特性 - RabbitMQ信任package设置</a></p>
                            <p class="item-date"><time datetime="2018-03-12T16:00:00.000Z" itemprop="datePublished">2018-03-13</time></p>
                        </div>
                    </li>
                
            </ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">分类</h3>
        <div class="widget">
            <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/QueryDSL-核心技术/">QueryDSL 核心技术</a><span class="category-list-count">7</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/SpringBoot-核心技术/">SpringBoot 核心技术</a><span class="category-list-count">36</span></li></ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">归档</h3>
        <div class="widget">
            <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/04/">四月 2018</a><span class="archive-list-count">3</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/03/">三月 2018</a><span class="archive-list-count">4</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/01/">一月 2018</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/12/">十二月 2017</a><span class="archive-list-count">4</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/11/">十一月 2017</a><span class="archive-list-count">2</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/10/">十月 2017</a><span class="archive-list-count">2</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/09/">九月 2017</a><span class="archive-list-count">4</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/08/">八月 2017</a><span class="archive-list-count">4</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/07/">七月 2017</a><span class="archive-list-count">9</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/06/">六月 2017</a><span class="archive-list-count">2</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/05/">五月 2017</a><span class="archive-list-count">3</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/04/">四月 2017</a><span class="archive-list-count">5</span></li></ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">标签</h3>
        <div class="widget">
            <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/QueryDSL/">QueryDSL</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/SpringBoot/">SpringBoot</a><span class="tag-list-count">43</span></li></ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">标签云</h3>
        <div class="widget tagcloud">
            <a href="/tags/QueryDSL/" style="font-size: 10px;">QueryDSL</a> <a href="/tags/SpringBoot/" style="font-size: 20px;">SpringBoot</a>
        </div>
    </div>

    
        
    <div class="widget-wrap widget-list">
        <h3 class="widget-title">链接</h3>
        <div class="widget">
            <ul>
                
                    <li>
     				<a href="http://hexo.io" target="_blank">Hexo</a>
					</li>
                
            </ul>
        </div>
    </div>


    
    <div id="toTop" class="fa fa-angle-up"></div>
</aside>

				
        </div>
        <!--引入不蒜子-->
<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<footer id="footer">
    <div class="outer">
        <div id="footer-info" class="inner">
            <span  id="busuanzi_container_site_pv">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
            &copy; 2017 - 2018 恒宇少年 - 版权所有<br>
        </div>
    </div>
</footer>

        


    
        <script src="/libs/lightgallery/js/lightgallery.min.js"></script>
        <script src="/libs/lightgallery/js/lg-thumbnail.min.js"></script>
        <script src="/libs/lightgallery/js/lg-pager.min.js"></script>
        <script src="/libs/lightgallery/js/lg-autoplay.min.js"></script>
        <script src="/libs/lightgallery/js/lg-fullscreen.min.js"></script>
        <script src="/libs/lightgallery/js/lg-zoom.min.js"></script>
        <script src="/libs/lightgallery/js/lg-hash.min.js"></script>
        <script src="/libs/lightgallery/js/lg-share.min.js"></script>
        <script src="/libs/lightgallery/js/lg-video.min.js"></script>
    
    
        <script src="/libs/justified-gallery/jquery.justifiedGallery.min.js"></script>
    
    



<!-- Custom Scripts -->
<script src="/js/main.js"></script>

    </div>
</body>
</html>